// SPDX-License-Identifier: Mulan PSL v2
/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This software is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::error::Error as StdError;

/// A trait for a protocol that breaks a continuous stream of bytes into discrete frames.
///
/// This trait provides a high-performance, largely zero-copy interface for
/// both encoding new frames and decoding frames from a byte buffer.
pub trait Framer {
    /// The type of error that may occur during encoding or decoding a frame.
    type Error: StdError + Send + Sync + 'static;

    /// Returns the constant length of the frame header in bytes.
    ///
    /// The primary use case for this function is to allow callers to efficiently
    /// pre-allocate a buffer of the exact required size before calling `encode_frame`.
    fn header_len(&self) -> usize;

    /// Prepares the buffer for encoding by splitting it into header and payload areas.
    ///
    /// This method must be called before writing the payload. It returns two distinct mutable
    /// slices for the header and the payload. After this call, the caller must write the
    /// payload and then call `encode_frame` to finalize the process.
    fn prepare_buffer<'a>(
        &self,
        buff: &'a mut [u8],
    ) -> Result<(&'a mut [u8], &'a mut [u8]), Self::Error>;

    /// Encodes the frame by writing the header after the payload is written.
    ///
    /// This method completes the encoding process initiated by `reserve_header`. It uses the
    /// provided `payload_len` to fill in the header fields (like length and checksum)
    /// based on the payload data that now resides in the buffer.
    ///
    /// This method **must** be called after the payload has been written to the slice
    /// obtained from `reserve_header`.
    ///
    /// # Parameters
    /// - `header`: The frame header buffer.
    /// - `payload`: The payload slice, which now contains the payload.
    ///
    /// # Returns
    /// - `Ok(usize)`: The total size of the whole frame (header + payload).
    /// - `Err(Self::Error)`: If an error occurs during finalization.
    fn encode_frame<'a>(
        &self,
        header: &'a mut [u8],
        payload: &'a [u8],
    ) -> Result<usize, Self::Error>;

    /// Decodes a frame from the buffer.
    ///
    /// # Parameters
    /// - `buff`: A byte slice containing the raw data stream.
    ///
    /// # Returns
    /// This method returns a `Result` with three possible outcomes:
    /// - `Ok(Some((consumed, payload)))`: A complete frame was successfully decoded.
    ///   - `consumed`: The total number of bytes that the complete frame occupies in `buff`.
    ///   - `payload`: A slice pointing to the payload portion of the frame within the original buff.
    /// - `Ok(None)`: The buffer does not contain enough data to form a complete frame. The caller should wait for more data.
    /// - `Err(Self::Error)`: An error occurred during the frame decoding.
    fn decode_frame<'a>(&self, buff: &'a [u8]) -> Result<Option<(usize, &'a [u8])>, Self::Error>;
}

mod generic;
pub use generic::*;

mod raw;
pub use raw::*;

#[cfg(test)]
mod tests;
